package client

import (
	"fmt"
	"sync"
	"time"

	"starsine.cn/appTpl/cmn"
	. "starsine.cn/appTpl/cmn/defs"
	"starsine.cn/appTpl/cmn/logs"
	"starsine.cn/appTpl/cmn/timermgr"
)

type SvcClientMng struct {
	data map[string]*GRpcClient

	tmID int64
	tm   *timermgr.TimerMgr

	sync.RWMutex
	appCtxt *AppContext
}

func CreateSvcClientMng(appCtxt *AppContext) *SvcClientMng {
	p := new(SvcClientMng)

	p.data = make(map[string]*GRpcClient)

	p.tmID = -1
	p.tm = cmn.GetTm()
	p.appCtxt = appCtxt

	return p
}

func (p *SvcClientMng) Dump() (str string) {
	p.RWMutex.RLock()
	defer p.RWMutex.RUnlock()

	str += fmt.Sprintln("SvcClientMng:")
	for k, v := range p.data {
		str += fmt.Sprintf("[instance id: %s] %s\n", k, v.String())
	}

	return str
}

func (p *SvcClientMng) Start() {
	p.checkPeriod()
}

func (p *SvcClientMng) End() {
	p.tm.CancelTimer(p.tmID)
}

func (p *SvcClientMng) checkAndRecycle(params interface{}) {
	p.Recycle()
}

func (p *SvcClientMng) checkPeriod() {
	p.tmID = p.tm.AddPeriodTimer(
		time.Duration(10),
		timermgr.NewOnTimeOut(p.checkAndRecycle,
			nil,
		))
}

func (p *SvcClientMng) Get(k, addr string) *GRpcClient {
	p.RWMutex.RLock()
	if v, ok := p.data[k]; ok {
		p.RWMutex.RUnlock()
		return v
	}
	p.RWMutex.RUnlock()

	p.RWMutex.Lock()
	v := CreateGRpcClient(p.appCtxt, addr)
	err := v.Start()
	if err != nil {
		p.RWMutex.Unlock()
		logs.Warn("CreateGRpcClient(%s) fail, err: %s", addr, err)
		return nil
	}
	p.data[k] = v
	p.RWMutex.Unlock()

	logs.Debugf("SvcClientMng get(%s)", k)
	return v
}

func (p *SvcClientMng) Push(k string) {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()
	v, ok := p.data[k]
	if ok {
		v.End()
		delete(p.data, k)
		logs.Debugf("SvcClientMng push(%s)", k)
	} else {
		logs.Errorf("deleting an unexist Strung(%s)", k)
	}
}

func (p *SvcClientMng) Recycle() {
	for k, v := range p.data {
		if v.IsExpired() {
			p.Push(k)
		}
	}
}
